/*! 
    \file    main.c 
    \brief   使用DMA中断进行数据收发 
 
    \version 2025-08-08, V1.1.0, 适用于gd32c2x1的固件 
*/

/* 
    版权所有 (c) 2025, GigaDevice Semiconductor Inc. 
 
    源代码和二进制形式的再分发和使用，无论是否经过修改，均允许，前提是满足以下条件： 
 
    1. 源代码的再分发必须保留上述版权声明、本条件列表和以下免责声明。 
    2. 二进制形式的再分发必须在随发行版提供的文档和/或其他材料中复制上述版权声明、本条件列表和以下免责声明。 
    3. 未经事先书面许可，不得使用版权持有者或其贡献者的名称为本软件派生的产品做推广、背书或宣传。 
 
    本软件由版权持有者和贡献者按“原样”提供，不提供任何明示或暗示的担保，包括但不限于对适销性和特定用途适用性的暗示担保。 
    在任何情况下，版权持有者或贡献者均不对因使用本软件而引起的任何直接、间接、偶然、特殊、惩罚性或后果性损害（包括但不限于采购替代商品或服务、使用损失、数据或利润损失、业务中断等）承担责任，无论是在合同诉讼、严格责任或侵权（包括疏忽或其他原因）中，即使已被告知发生此类损害的可能性。 
*/

#include "gd32c2x1.h"
#include "systick.h"
#include <stdio.h>
#include "gd32c231c_eval.h"

#define USART0_RDATA_ADDRESS      (&USART_RDATA(USART0))
#define USART0_TDATA_ADDRESS      (&USART_TDATA(USART0))
#define ARRAYNUM(arr_nanme)       (uint32_t)(sizeof(arr_nanme) / sizeof(*(arr_nanme)))

__IO FlagStatus g_transfer_complete = RESET;
uint8_t rxbuffer[10];
uint8_t txbuffer[] = "\n\rUSART DMA interrupt receive and transmit example, please input 10 bytes:\n\r";

void com_usart_init(void);
void nvic_config(void);

/*! 
    \brief      主函数 
    \param[in]  无 
    \param[out] 无 
    \retval     无 
*/
int main(void)
{
    dma_parameter_struct dma_init_struct;
    /* 使能DMA时钟 */
    rcu_periph_clock_enable(RCU_DMA);
    rcu_periph_clock_enable(RCU_DMAMUX);
    /* 初始化串口 */
    com_usart_init();
    /* 配置DMA中断 */
    nvic_config();

    /* 初始化DMA通道0 */
    dma_deinit(DMA_CH0);
    dma_struct_para_init(&dma_init_struct);
    dma_init_struct.request      = DMA_REQUEST_USART0_TX;
    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr  = (uint32_t)txbuffer;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number       = ARRAYNUM(txbuffer);
    dma_init_struct.periph_addr  = (uint32_t)USART0_TDATA_ADDRESS;
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH0, &dma_init_struct);

    /* 配置DMA模式 */
    dma_circulation_disable(DMA_CH0);
    dma_memory_to_memory_disable(DMA_CH0);
    /* 禁用DMAMUX_MUXCH0同步模式 */
    dmamux_synchronization_disable(DMAMUX_MUXCH0);
    /* 使能USART的DMA发送功能 */
    usart_dma_transmit_config(USART0, USART_TRANSMIT_DMA_ENABLE);
    /* 使能DMA通道0传输完成中断 */
    dma_interrupt_enable(DMA_CH0, DMA_INT_FTF);
    /* 使能DMA通道0 */
    dma_channel_enable(DMA_CH0);

    /* 初始化DMA通道1 */
    dma_deinit(DMA_CH1);
    dma_struct_para_init(&dma_init_struct);
    dma_init_struct.request      = DMA_REQUEST_USART0_RX;
    dma_init_struct.direction    = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr  = (uint32_t)rxbuffer;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number       = ARRAYNUM(rxbuffer);
    dma_init_struct.periph_addr  = (uint32_t)USART0_RDATA_ADDRESS;
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH1, &dma_init_struct);

    /* 配置DMA模式 */
    dma_circulation_disable(DMA_CH1);
    dma_memory_to_memory_disable(DMA_CH1);
    /* 禁用DMAMUX_MUXCH1同步模式 */
    dmamux_synchronization_disable(DMAMUX_MUXCH1);
    /* 使能USART的DMA接收功能 */
    usart_dma_receive_config(USART0, USART_RECEIVE_DMA_ENABLE);
    /* 使能DMA通道1传输完成中断 */
    dma_interrupt_enable(DMA_CH1, DMA_INT_FTF);
    /* 使能DMA通道1 */
    dma_channel_enable(DMA_CH1);

    /* 等待传输完成 */
    while(RESET == g_transfer_complete) {
    }

    g_transfer_complete = RESET;

    /* 等待传输完成 */
    while(RESET == g_transfer_complete) {
    }

    printf("\n\r%s\n\r", rxbuffer);
    while(1) {
    }
}

/*! 
    \brief      配置USART 
    \param[in]  无 
    \param[out] 无 
    \retval     无 
*/
void com_usart_init(void)
{
    /* 使能USART时钟 */
    rcu_periph_clock_enable(RCU_USART0);

    /* 使能串口GPIO时钟 */
    rcu_periph_clock_enable(RCU_GPIOA);

    /* 连接端口到USART TX */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);

    /* 连接端口到USART RX */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);

    /* 配置USART TX为复用推挽输出 */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_LEVEL_1, GPIO_PIN_9);

    /* 配置USART RX为复用推挽输出 */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_LEVEL_1, GPIO_PIN_10);

    /* 配置USART参数 */
    usart_deinit(USART0);
    usart_baudrate_set(USART0, 115200U);
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);

    usart_enable(USART0);
}

/*! 
    \brief      配置DMA中断 
    \param[in]  无 
    \param[out] 无 
    \retval     无 
*/
void nvic_config(void)
{
    nvic_irq_enable(DMA_Channel0_IRQn, 0);
    nvic_irq_enable(DMA_Channel1_IRQn, 0);
}
